package com.danbay.cloud.deviceaccess.service;

import com.danbay.cloud.base.constants.Const;
import com.danbay.cloud.device.api.dto.ReportDataDto;
import com.danbay.cloud.deviceaccess.api.dto.*;
import com.danbay.cloud.deviceaccess.api.dto.collector.*;
import com.danbay.cloud.deviceaccess.api.dto.lock.*;
import com.danbay.cloud.deviceaccess.api.service.ICtrlDeviceService;
import com.danbay.cloud.deviceaccess.client.DeviceRegServiceClient;
import com.danbay.cloud.deviceaccess.client.LockDeviceReportClient;
import com.danbay.cloud.deviceaccess.client.MeterDeviceReportClient;
import com.danbay.cloud.lock.device.api.dto.AliasDto;
import com.danbay.framework.core.KeyValuePair;
import com.danbay.framework.exception.BusinessException;
import com.danbay.framework.kafka.KafkaClient;
import com.danbay.framework.kafka.KafkaContext;
import com.danbay.framework.springnc.annotation.RequestMapping;
import com.danbay.framework.utils.DateUtils;
import com.danbay.framework.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.annotation.PostConstruct;

/**
 * @author SJG
 *         2017/11/8.
 */
@Service
@Slf4j
public class CtrlDeviceService implements ICtrlDeviceService {

    private final DeviceCacheService deviceCacheService;

    private final DeviceRegServiceClient deviceRegServiceClient;

    private final MeterDeviceReportClient meterDeviceReportClient;

    private KafkaClient kafkaClient;
    private final LockDeviceReportClient deviceInfoServiceClient;


    @Autowired
    public CtrlDeviceService(DeviceCacheService deviceCacheService, DeviceRegServiceClient deviceRegServiceClient, KafkaClient kafkaClient, LockDeviceReportClient deviceInfoServiceClient, MeterDeviceReportClient meterDeviceReportClient) {
        this.deviceCacheService = deviceCacheService;
        this.deviceRegServiceClient = deviceRegServiceClient;
        this.kafkaClient = kafkaClient;
        this.deviceInfoServiceClient = deviceInfoServiceClient;
        this.meterDeviceReportClient = meterDeviceReportClient;
    }

    @PostConstruct
    public void init() {
        //重启程序后恢复设备与主题的映射关系
        deviceCacheService.recoverDeviceTopicMapping();
    }

    /**
     * 中控下发允许设备入网指令
     * @param devId 中控设备id
     * @throws BusinessException 业务异常
     */
    @Override
    public void sendAllow(String devId) throws BusinessException {
        checkReg(devId);
        KafkaContext.requestTopic(kafkaClient, devId).postCONWithUriQuery(Const.DEVICE_CTRL_ALLOW_AP, getCtrlDevIdParam(devId));
        log.info("下发允许入网指令成功,cmd:{},devId:{}",
                Const.DEVICE_CTRL_ALLOW_AP, devId);
    }

    /**
     * 中控允许设备入网后的中控回复消息
     * @param allowAp 允许入网回复信息
     * @throws BusinessException 业务异常
     */
    @Override
    public void ackAllow(AllowAp allowAp) throws BusinessException {
        log.info("收到允许入网命令的中控devId:{}的回复", allowAp.getDevId());
    }

    /**
     * 重启中控
     * @param devId 中控设备id
     * @throws BusinessException 业务异常
     */
    @Override
    public void sendRebootGateway(String devId) throws BusinessException {
        checkReg(devId);
        KafkaContext.requestTopic(kafkaClient, devId).postCONWithUriQuery(Const.DEVICE_CTRL_REBOOT, getCtrlDevIdParam(devId));
        log.info("下发重启中控指令成功,cmd:{},devId:{}",
                Const.DEVICE_CTRL_REBOOT, devId);
    }

    /**
     * 重启中控回复
     * @param rebootGw 重启中控回复信息
     * @throws BusinessException 业务异常
     */
    @Override
    @RequestMapping(value = Const.DEVICE_CTRL_REBOOT, method = RequestMethod.GET)
    public void ackRebootGateway(RebootGw rebootGw) throws BusinessException {
        log.info("收到重启中控命令的中控devId:{}的回复", rebootGw.getDevId());
    }

    /**
     * 远程获取水表计量信息
     * @param devId 水表设备id
     * @throws BusinessException 业务异常
     */
//    @Override
//    public void sendGetWaterData(String devId) throws BusinessException {
//        checkReg(devId);
//        KafkaContext.requestTopic(kafkaClient, devId).postCONWithUriQuery(Const.DEVICE_CTRL_GET_WATER, getCtrlDevIdParam(devId));
//        log.info("下发远程获取水表计量信息指令成功,cmd:{},devId:{}",
//                Const.DEVICE_CTRL_GET_WATER, devId);
//    }

    /**
     * 远程获取水表计量信息
     * @param getWater 水表计量信息
     * @throws BusinessException  业务异常
     */
    @Override
    public void getWaterData(GetWater getWater) throws BusinessException {
        checkReg(getWater.getDevId());
        meterDeviceReportClient.freezeData(ReportDataDto.builder().devId(getWater.getDevId())
                                                            .meterCount(getWater.getValue())
                                                            .date(DateUtils.now(DateUtils.YMDHMS2)).build());
        log.info("远程获取水表计量信息到云端成功!devId:{},meterCount:{}",getWater.getDevId(),getWater.getValue());
    }

    /**
     * 下发水电表取日冻结数据指令
     * @param devId 水表设备id
     * @param ymd   年月日,如171122
     * @throws BusinessException 业务异常
     */
    @Override
    public void sendGetDayData(String devId, String ymd) throws BusinessException {
        checkReg(devId);
        if (StringUtils.isBlank(ymd) || ymd.length() != 6) {
            throw new BusinessException("ymd参数格式不正解,格式应为yyMMdd,如:171122");
        }
        KafkaContext.requestTopic(kafkaClient, devId).postCONWithUriQuery(Const.DEVICE_CTRL_GET_DAY_DATA, getCtrlDevIdParam(devId), new KeyValuePair<>("YMD", ymd));
        log.info("下发水电表取日冻结数据指令成功,cmd:{},devId:{},date:{}",
                Const.DEVICE_CTRL_GET_DAY_DATA, devId, ymd);
    }

    /**
     * 上报水电表取日冻结数据
     * @param dayData 日冻结数据
     * @throws BusinessException
     */
    @Override
    public void getDayData(GetDayData dayData) throws BusinessException {
        checkReg(dayData.getDevId());
        String reportDate = DateUtils.parse(dayData.getYmd(), "yyMMdd").toString(DateUtils.YMDHMS2);
        meterDeviceReportClient.freezeData(ReportDataDto.builder().devId(dayData.getDevId())
                                                        .meterCount(dayData.getValue())
                                                        .date(reportDate).build());
        log.info("上报水电表取日冻结数据到云端成功!devId:{},meterCount:{},date:{}", dayData.getDevId(), dayData.getValue(), dayData.getYmd());
    }

    /**
     * 远程获取电表电能计量信息
     * @param devId 电表设备id
     * @throws BusinessException 业务异常
     */
    @Override
    public void sendGetStatusKwh(WiredMeterNumber wiredMeterNumber,String devId) throws BusinessException {
        checkReg(devId);
        KafkaContext.requestTopic(kafkaClient, devId).postCONWithUriQuery(Const.DEVICE_CTRL_GET_STATUS_KWH, getCtrlDevIdParam(devId));
        log.info("下发获取电表电能指令成功,cmd:{},devId:{}", Const.DEVICE_CTRL_GET_STATUS_KWH, devId);
    }

    /**
     * 云端远程控制电表合闸命令
     * @param devId 电表设备id
     * @throws BusinessException 业务异常
     */
    @Override
    public void sendEmSwitchOpen(MeterOpen meterOpen, String devId) throws BusinessException {
        checkReg(devId);
        KafkaContext.requestTopic(kafkaClient, devId).postCONWithUriQuery(Const.DEVICE_CTRL_EM_SWITCH_OPEN, getCtrlDevIdParam(devId));
        log.info("下发电表电闸合闸指令成功,cmd:{},devId:{}", Const.DEVICE_CTRL_EM_SWITCH_OPEN, devId);
    }

    /**
     * 云端远程控制电表拉闸命令
     * @param devId 电表设备id
     * @throws BusinessException 业务异常
     */
    @Override
    public void sendEmSwitchClose(String devId) throws BusinessException {
        checkReg(devId);
        KafkaContext.requestTopic(kafkaClient, devId).postCON(Const.DEVICE_CTRL_EM_SWITCH_CLOSE, getCtrlDevIdParam(devId));
        log.info("下发电表电闸拉闸指令成功,cmd:{},devId:{}", Const.DEVICE_CTRL_EM_SWITCH_CLOSE, devId);
    }

    /**
     * 云端远程获取电表电压测量信息
     * @param devId 电表设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void getStatusVolt(String devId) throws BusinessException {

    }

    /**
     * 云端远程获取电表瞬时功率
     * @param devId 电表设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void getStatusPower(String devId) throws BusinessException {

    }

    /**
     * 云端远程获取电表电流测量信息
     * @param devId 电表设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void getStatusCurrent(String devId) throws BusinessException {

    }

    /**
     * 云端远程获取电表瞬时功率
     * @param devId 电表设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void getStatusPF(String devId) throws BusinessException {

    }

    /**
     * 门锁新增密码信息
     * @param newPwd               payload内容
     * @param deviceId             设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void addNewPwd(NewPwd newPwd, String deviceId) throws BusinessException {
        checkReg(deviceId);
        log.info("设备:{}新增密码信息,type:{},alias:{}", deviceId,
                newPwd.getType(), newPwd.getAlias());
        String messageId = KafkaContext.requestTopic(kafkaClient, deviceId).postCON(Const.DEVICE_CTRL_LOCK_ADD_NEW_PWD, newPwd,
                getCtrlDevIdParam(deviceId));
        //根据设备ID与密码别名缓存messageId
        deviceCacheService.setMsgId(deviceId + "_" + newPwd.getAlias(), messageId);
    }

    /**
     * 修改门锁密码
     * @param editPwd             payload内容
     * @param deviceId            设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void editPwd(EditPwd editPwd, String deviceId) throws BusinessException {
        checkReg(deviceId);
        log.info("设备:{}修改密码信息,type:{},alias:{}", deviceId,
                editPwd.getType(), editPwd.getAlias());
        String messageId = KafkaContext.requestTopic(kafkaClient, deviceId).postCON(Const.DEVICE_CTRL_LOCK_UPDATE_PWD, editPwd,
                getCtrlDevIdParam(deviceId));
        //根据设备ID与密码别名缓存messageId
        deviceCacheService.setMsgId(deviceId + "_" + editPwd.getAlias(), messageId);
    }

    /**
     * 删除门锁密码
     * @param delPwd        payload内容
     * @param deviceId      设备id
     * @throws BusinessException   业务异常
     */
    @Override
    public void delPwd(DelPwd delPwd, String deviceId) throws BusinessException {
        checkReg(deviceId);
        log.info("设备:{}删除密码信息,type:{},alias:{}", deviceId,
                delPwd.getType(), delPwd.getAlias());
        String messageId = KafkaContext.requestTopic(kafkaClient, deviceId).postCON(Const.DEVICE_CTRL_LOCK_DEL_PWD, delPwd,
                getCtrlDevIdParam(deviceId));
        //根据设备ID与密码别名缓存messageId
        deviceCacheService.setMsgId(deviceId + "_" + delPwd.getAlias(), messageId);
    }

    /**
     * 停用/启用门锁密码
     * @param stopPwd       payload内容
     * @param deviceId      设备id
     * @throws BusinessException    业务异常
     */
    @Override
    public void stopPwd(StopPwd stopPwd, String deviceId) throws BusinessException {
        checkReg(deviceId);
        log.info("设备:{}启用/停用密码信息,type:{},alias:{}", deviceId,
                stopPwd.getType(), stopPwd.getAlias());
        String messageId = KafkaContext.requestTopic(kafkaClient, deviceId).postCON(Const.DEVICE_CTRL_LOCK_STOP_PWD, stopPwd,
                getCtrlDevIdParam(deviceId));
        //根据设备ID与密码别名缓存messageId
        deviceCacheService.setMsgId(deviceId + "_" + stopPwd.getAlias(), messageId);
    }

    /**
     * 设备-门锁 更新密码别名
     *
     * @param deviceId
     * @param updateAlias
     * @throws BusinessException
     */
    @Override
    public void updateAlias(String deviceId, UpdateAlias updateAlias) throws BusinessException {
        checkReg(deviceId);
        log.info("设备:{}更新密码别名,type:{},oldAlias:{},newAlias:{}", deviceId,
                updateAlias.getType(), updateAlias.getOldAlias(), updateAlias.getNewAlias());
        String messageId = KafkaContext.requestTopic(kafkaClient, deviceId).postCON(Const.DEVICE_CTRL_LOCK_DEL_PWD, updateAlias,
                getCtrlDevIdParam(deviceId));
    }

    /**
     * 设备-门锁  获取密码别名
     * @param deviceId  设备id
     * @throws BusinessException
     */
    @Override
    public void getAlias(String deviceId) throws BusinessException {
        checkReg(deviceId);
        log.info("设备-门锁:{}下发获取密码别名命令", deviceId);
        KafkaContext.requestTopic(kafkaClient, deviceId).getCON(Const.DEVICE_CTRL_LOCK_GET_ALIAS, null, getCtrlDevIdParam(deviceId));
    }

    /**
     * 设备-门锁获取密码别名内容上报
     * @param alias  payload内容
     * @throws BusinessException
     */
    @Override
    public void getAlias(Alias alias) throws BusinessException {
        log.info("设备-门锁:{}上报的密码别名列表");
        deviceInfoServiceClient.getAlias(AliasDto.builder()
                .deviceId(alias.getDeviceId())
                .user(alias.getUser())
                .kind(alias.getKind())
                .stat(alias.getStat()).build());
    }

    /**
     * 获取采集器下接档案数
     * @param deviceId 设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void getCount(String deviceId) throws BusinessException {
        log.info("设备-采集器:{}获取下接表档案数",deviceId);
        KafkaContext.requestTopic(kafkaClient, deviceId).getCON(Const.DEVICE_COLLECTOR_GET_COUNT,getCtrlDevIdParam(deviceId));
    }

    /**
     * 采集器电表获取日冻结数据
     * @param dailyData  payload内容
     * @param deviceId   设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void getDailyData(GetDailyData dailyData, String deviceId) throws BusinessException {
        log.info("设备-采集器:{}下发获取日冻结读数指令:{}",deviceId,dailyData.getNumber());
        KafkaContext.requestTopic(kafkaClient, deviceId).getCON(Const.DEVICE_COLLECTOR_GET_DAILY_DATA,dailyData,getCtrlDevIdParam(deviceId));
    }

    /**
     * 采集器获取电表整点冻结数据
     * @param dailyData  payload内容
     * @param deviceId 设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void getHourlyData(GetDailyData dailyData, String deviceId) throws BusinessException {
        log.info("设备-采集器:{}获取电表整点冻结数据:{}",deviceId,dailyData.getNumber());
        KafkaContext.requestTopic(kafkaClient, deviceId).getCON(Const.DEVICE_COLLECTOR_GET_HOURLY_DATA,dailyData,getCtrlDevIdParam(deviceId));
    }

    /**
     * 远程获取采集器
     * @param getMeter  payload内容
     * @param deviceId  设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void getMeter(GetMeter getMeter, String deviceId) throws BusinessException {
        log.info("设备-采集器:{}获取电表整点冻结数据:{}",deviceId,getMeter.getIndex());
        KafkaContext.requestTopic(kafkaClient, deviceId).getCON(Const.DEVICE_COLLECTOR_GET_METER,getMeter,getCtrlDevIdParam(deviceId));
    }

    /**
     * 远程增加电表采集器建档
     * @param wiredMeter  payload内容
     * @param deviceId    设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void addMeter(WiredMeter wiredMeter, String deviceId) throws BusinessException {
        log.info("设备-采集器:{}增加远程电表建档信息:type:{},addr:{},port:{}",deviceId,wiredMeter.getType(),
                                                wiredMeter.getAddr(),wiredMeter.getPort());
        KafkaContext.requestTopic(kafkaClient, deviceId).postCON(Const.DEVICE_COLLECTOR_ADD_METER,wiredMeter,getCtrlDevIdParam(deviceId));
    }

    /**
     * 采集器删除电表档案
     * @param wiredMeterAddr payload内容
     * @param deviceId       设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void delMeter(WiredMeterAddr wiredMeterAddr, String deviceId) throws BusinessException {
        log.info("设备-采集器:{}删除远程电表建档信息:addr:{}",deviceId,wiredMeterAddr.getAddr());
        KafkaContext.requestTopic(kafkaClient, deviceId).postCON(Const.DEVICE_COLLECROT__DEL_METER,wiredMeterAddr,getCtrlDevIdParam(deviceId));
    }

    /**
     * 水表采集器获取水量信息
     * @param wiredMeterNumber  payload内容
     * @param deviceId          设备id
     * @throws BusinessException  业务异常
     */
    @Override
    public void getWater(WiredMeterNumber wiredMeterNumber, String deviceId) throws BusinessException {
        log.info("设备-采集器:{}获取水量信息:{}",deviceId,wiredMeterNumber.getNumber());
        KafkaContext.requestTopic(kafkaClient, deviceId).getCON(Const.DEVICE_CTRL_GET_WATER,wiredMeterNumber,getCtrlDevIdParam(deviceId));
    }

    /**
     * 检查设备是否注册
     * @param devId 设备id
     * @throws BusinessException
     */
    private void checkReg(String devId) throws BusinessException {
        if (StringUtils.isBlank(devId) || !deviceCacheService.devIdIsRegistered(devId)) {
            throw new BusinessException("设备devId:" + devId + "为空或还未注册!");
        }
    }

    private boolean checkOldDev(String devId) throws BusinessException{
        if (StringUtils.isBlank(devId) || !deviceCacheService.devIdIsRegistered(devId)){
            if (deviceCacheService.isDeviceOnline(devId)){
                return true;
            }else {
                throw new BusinessException("设备devId:" + devId + "为空或还未注册!");
            }
        }
        return false;
    }
    /**
     * 取得控制命令中的devId参数
     * @param devId 设备id
     * @return KeyValuePair
     */
    private KeyValuePair<String> getCtrlDevIdParam(String devId) {
        return new KeyValuePair<>("dev_id", devId);
    }

}
